Skip to content

箭头函数与 this 面试题全解析

一、核心要点速览

💡 核心考点

  • this 绑定: 箭头函数的 this 继承自定义时的上下文
  • 与普通函数区别: 箭头函数没有自己的 this、arguments、super、new.target
  • 适用场景: 回调函数、数组方法、保持 this 上下文
  • 不适用场景: 对象方法、构造函数、需要动态 this 的场景

二、箭头函数基础语法

1. 基本用法

javascript
// 普通函数
function add(a, b) {
  return a + b
}

// 箭头函数
const add = (a, b) => {
  return a + b
}

// 简化写法(单表达式自动返回)
const add = (a, b) => a + b

// 单个参数省略括号
const double = x => x * 2

// 无参数
const sayHi = () => console.log('Hi')

// 返回对象(需要括号)
const getUser = () => ({ name: 'Vue', age: 3 })

// 多语句(需要花括号和 return)
const process = (data) => {
  const result = transform(data)
  return result
}

2. 语法对比

javascript
// ========== 普通函数 ==========
function fn1(a, b) {
  return a + b
}

// ========== 箭头函数 ==========
const fn2 = (a, b) => a + b

// ========== 默认参数 ==========
function fn3(a, b = 10) {
  return a + b
}

const fn4 = (a, b = 10) => a + b

// ========== 剩余参数 ==========
function fn5(...args) {
  return args.reduce((sum, val) => sum + val, 0)
}

const fn6 = (...args) => args.reduce((sum, val) => sum + val, 0)

// ========== 解构参数 ==========
function fn7({ name, age }) {
  return `${name}, ${age}`
}

const fn8 = ({ name, age }) => `${name}, ${age}`

三、this 绑定规则详解

1. 普通函数 vs 箭头函数

javascript
// 普通函数:this 指向调用者
const obj1 = {
  name: 'obj1',
  getName() {
    return this.name
  }
}

obj1.getName()        // 'obj1' (this → obj1)
const fn = obj1.getName
fn()                  // undefined (this → window)

// 箭头函数:this 指向定义时的上下文
const obj2 = {
  name: 'obj2',
  getName: () => {
    return this.name // this → window/undefined
  }
}

obj2.getName()  // undefined (this 指向全局)

2. this 指向对比图

┌──────────────────────────────────────────────────────────┐
│                  箭头函数 vs 普通函数                     │
└──────────────────────────────────────────────────────────┘

普通函数的 this:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
const obj = {
  name: 'MyObj',
  sayName: function() {
    console.log(this.name)
  }
}

obj.sayName()        // 'MyObj' (this → obj)
const fn = obj.sayName
fn()                 // undefined (this → window)
setTimeout(fn, 100)  // undefined (this → window)

this 绑定规则:
  ┌────────────────────────────────┐
  │ 谁调用,this 指向谁             │
  │                                │
  │ obj.sayName() → this = obj    │
  │ fn() → this = window          │
  │ new Fn() → this = 新实例       │
  │ call/apply → this = 指定对象   │
  └────────────────────────────────┘
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

箭头函数的 this:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
const obj = {
  name: 'MyObj',
  sayName: () => {
    console.log(this.name)
  }
}

obj.sayName()  // undefined (this → window)

this 绑定规则:
  ┌────────────────────────────────┐
  │ 箭头函数没有自己的 this        │
  │ 继承自定义时上下文的 this      │
  │                                │
  │ 在全局 → this = window        │
  │ 在对象 → this = 外层 this      │
  │ 在 class → this = 实例         │
  └────────────────────────────────┘
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

3. 经典面试题

javascript
// 题目 1:普通函数
const obj1 = {
  name: 'obj1',
  getName() {
    setTimeout(function() {
      console.log(this.name) // undefined
    }, 100)
  }
}
obj1.getName()

// 原因:setTimeout 的回调中 this → window

// 题目 2:箭头函数
const obj2 = {
  name: 'obj2',
  getName() {
    setTimeout(() => {
      console.log(this.name) // 'obj2'
    }, 100)
  }
}
obj2.getName()

// 原因:箭头函数的 this 继承自 getName 方法(→ obj2)

// 题目 3:嵌套箭头函数
const obj3 = {
  name: 'obj3',
  getName() {
    const fn = () => {
      const inner = () => {
        console.log(this.name)
      }
      inner()
    }
    fn()
  }
}
obj3.getName() // 'obj3'

// 原因:所有箭头函数都继承自同一 this

四、经典应用场景

1. 保持 this 上下文

javascript
// ✓ 场景 1: 定时器回调
class Timer {
  constructor() {
    this.seconds = 0
    
    // 箭头函数保持 this 为实例
    setInterval(() => {
      this.seconds++ // this 正确指向 Timer 实例
      console.log(this.seconds)
    }, 1000)
  }
  
  // ✗ 错误示范
  startBad() {
    setInterval(function() {
      this.seconds++ // this → window,错误!
    }, 1000)
  }
  
  // ✓ 正确示范(非箭头函数方案)
  startGood() {
    const self = this
    setInterval(function() {
      self.seconds++ // 保存 this 引用
    }, 1000)
  }
}

2. 数组方法

javascript
// ✓ 场景 2: 数组操作
[1, 2, 3].map(x => x * 2)           // [2, 4, 6]
[1, 2, 3].filter(x => x > 1)        // [2, 3]
[1, 2, 3].reduce((sum, x) => sum + x, 0) // 6

// ✓ 配合 this
class Calculator {
  constructor(multiplier) {
    this.multiplier = multiplier
  }
  
  multiply(arr) {
    return arr.map(x => x * this.multiplier)
    // this 正确指向 Calculator 实例
  }
}

const calc = new Calculator(2)
calc.multiply([1, 2, 3]) // [2, 4, 6]

3. DOM 事件处理

javascript
// ✓ 场景 3: 事件监听
class Button {
  constructor(element) {
    this.element = element
    this.clickCount = 0
    
    // 箭头函数保持 this
    this.element.addEventListener('click', (e) => {
      this.clickCount++
      this.handleClick(e)
    })
  }
  
  handleClick(e) {
    console.log(`Clicked ${this.clickCount} times`)
  }
}

// jQuery 风格
$('.button').on('click', (e) => {
  this.doSomething() // this 指向外部
})

4. Promise 链

javascript
// ✓ 场景 4: Promise 链式调用
class UserService {
  getUser(id) {
    return fetch(`/api/users/${id}`)
      .then(res => res.json())
      .then(user => {
        console.log(`User: ${user.name}`)
        return this.processUser(user)
        // this 正确指向 UserService
      })
      .catch(err => {
        console.error(this.errorMessage)
        throw err
      })
  }
}

五、不适用场景

1. 对象方法

javascript
// ✗ 错误:对象方法不要用箭头函数
const obj = {
  name: 'obj',
  greet: () => `Hello, ${this.name}` // this → window
}

obj.greet() // 'Hello, undefined'

// ✓ 正确:使用普通函数
const obj = {
  name: 'obj',
  greet() {
    return `Hello, ${this.name}`
  }
}

obj.greet() // 'Hello, obj'

2. 构造函数

javascript
// ✗ 错误:箭头函数不能用作构造函数
const Person = (name) => {
  this.name = name
}

const p = new Person('Vue') // TypeError: Person is not a constructor

// ✓ 正确:使用普通函数
function Person(name) {
  this.name = name
}

const p = new Person('Vue')

3. 原型方法

javascript
// ✗ 错误:原型方法用箭头函数
function Person(name) {
  this.name = name
}

Person.prototype.sayHi = () => {
  console.log(`Hi, I'm ${this.name}`) // this → window
}

const p = new Person('Vue')
p.sayHi() // "Hi, I'm undefined"

// ✓ 正确:使用普通函数
Person.prototype.sayHi = function() {
  console.log(`Hi, I'm ${this.name}`)
}

p.sayHi() // "Hi, I'm Vue"

4. arguments 对象

javascript
// ✗ 错误:箭头函数没有 arguments
const fn = () => {
  console.log(arguments) // ReferenceError
}

fn(1, 2, 3)

// ✓ 解决方案 1:使用剩余参数
const fn = (...args) => {
  console.log(args) // [1, 2, 3]
}

// ✓ 解决方案 2:使用普通函数
function fn() {
  console.log(arguments) // Arguments(3) [1, 2, 3]
}

六、箭头函数的限制

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
箭头函数不能用作:

1. ❌ 构造函数
   const Person = (name) => { this.name = name }
   new Person('Vue') // TypeError

2. ❌ 原型方法
   Person.prototype.sayHi = () => {}
   // this 不指向实例

3. ❌ arguments 对象
   const fn = () => arguments
   fn(1, 2, 3) // ReferenceError

4. ❌ yield 关键字
   // 不能作为 Generator 函数
   
5. ❌ 动态 this 场景
   - 对象方法
   - 事件处理器(需要 this → element)
   - 需要 call/apply/bind 的场景
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

七、面试标准回答

箭头函数是 ES6 引入的新函数形式,提供了更简洁的语法和不同的 this 绑定规则。

语法特点

  1. 更简洁:省略 function 关键字、单参数省略括号、单表达式自动返回
  2. 没有自己的 this、arguments、super、new.target
  3. 不能作为构造函数,不能使用 yield

this 绑定规则是箭头函数的核心特性:

  • 箭头函数没有自己的 this
  • this 继承自定义时的上下文(词法作用域)
  • 一旦定义,this 永远无法改变

与普通函数的区别

  • 普通函数:this 指向调用者,可以动态改变
  • 箭头函数:this 在定义时确定,无法改变

适用场景

  1. 回调函数(setTimeout、Promise.then)
  2. 数组方法(map、filter、reduce)
  3. 需要保持 this 上下文的场景

不适用场景

  1. 对象方法(需要动态 this)
  2. 构造函数(不能用 new)
  3. 原型方法(this 不指向实例)
  4. 需要 arguments 的场景

实际项目中,我主要用箭头函数写回调和数组操作,对象方法和构造函数仍然使用普通函数。这样既享受了箭头函数的便利,又避免了 this 陷阱。


八、记忆口诀

箭头函数歌诀:

箭头函数真简洁,
function 关键字省略。
没有自己的 this,
词法作用域来继承。

回调数组最适合,
对象方法要不得。
构造函数不能用,
arguments 也消失!

this 绑定要记清:
普通函数看调用,
箭头函数看定义,
场景选择最关键!

九、推荐资源


十、总结一句话

  • 箭头函数: 简洁语法 + 词法 this = 回调函数首选 🎯
  • this 绑定: 定义时确定 + 不可改变 = 避免 this 丢失
  • 使用场景: 回调数组适合 + 对象方法不适合 = 根据场景选择 ⚖️
最近更新